import 'dart:async';

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_demo/pages/page_with_title.dart';

import '../../widgets/app_widgets.dart';
import 'animated_widget_page.dart';

class AnimationPage extends PageWithTitle {
  const AnimationPage() : super("基础动画(Animation)");

  @override
  List<Widget> buildContentWidgets(BuildContext context) {
    return const [
      PrimaryTitle("简介"),
      BodyText("flutter的动画框架"),
      LabelText(
          "- Animation，负责驱动动画，本质是一个Listenable（类似于Android的LiveData）加"
              "上动画状态，可以监听获取动画状态、进度值"),
      LabelText(
          "- AnimatedWidget，负责实现动画效果，通过监听Listenable数值改变触发setState来"
              "重建组件，它有各种转换效果的实现类（Transition），Transition依赖Animation"
              "来驱动动画，参考："),
      PageLink(AnimatedWidgetPage()),
      AppSpacer.large,
      UlItem("Animation"),
      BodyText("动画基础类，本身是一个抽象类用于定义动画状态与进度的获取方式"),
      _FromValueListenableExample(),
      OlItem(1, "Animation.fromValueListenable"),
      BodyText("一个用于从Listenable生成动画对象的类，在Flutter框架中，有许多的状态是以"
          "Listenable表示的，比如ListView的控制器中包含的scrollPosition就是一个"
          "Listenable，我们通过它来构建Animation就可以实现列表滚动时执行一些动画"),
      LabelText(
          "- listenable，可以监听变化的状态对象，如果需要自己生成一个状态对象，可以使用"
              "ValueNotifier来创建，他与MutableLiveData差不多"),
      LabelText("- transformer，自定义转换器来实现状态值到动画进度的转变"),
      _TweenExample(),
      OlItem(2, "AnimationController"),
      BodyText("动画控制器，常用于实现跟随时间刷新的动画，通过监听屏幕刷帧触发动画更新，可以自"
          "行配置动画时长、循环、反转等效果，是我们最常用的动画驱动"),
      LabelText("- value，动画初始值"),
      LabelText("- duration，动画时长"),
      LabelText("- reverseDuration，设置动画执行末尾后方向执行的时长"),
      LabelText("- lowerBound，动画最小值，默认0"),
      LabelText("- upperBound，动画最大值，默认1"),
      LabelText(
          "- vsync，屏幕垂直同步，这里是TickerProvider（触发器提供者）类型，动画基本就是按"
              "照时间进行触发刷新的，比如60fps的动画就是每隔16毫秒刷新一次，而垂直同步就是与"
              "屏幕同步刷新，屏幕发送一个同步信号（vsync），系统绘制新画面，绘制的频率和时机"
              "由屏幕和系统决定。"),
      BodyText("Flutter已经提供了一些实现类可以用："),
      LabelText(
          "- SingleTickerProviderStateMixin，在StateWidget的State中混入此实现即可让"
              "state对象由作为触发器提供者的能力，该混入封装了组件状态跟踪实现屏幕同步刷新的监"
              "听与取消，开发者通过这个混入可以轻易实现屏幕同步刷新的触发器，而无需在对这个触发"
              "器进行生命周期管理（比如在组件不可见时取消监听，在组件可见时重新注册监听）"),
      LabelText("- TickerProviderStateMixin，在同个组件中需要使用到多个"
          "AnimationController时使用"),
      _TweenExample(),
      OlItem(3, "Tween"),
      BodyText(
          "可将double类型的动画转变成另一种类型，基本数据类型、Offset类型可以直接使用Tween"
              "进行转换，其它Size、Color、Matrix之类的转换也有对应的Tween实现类，其更重要"
              "的一点是可用于自定义动画数值区间[开始值、结束值]"),
      LabelText("- begin，开始值"),
      LabelText("- end，结束值"),
      LabelText("- animate，该方法接收一个Animation<double>，将其转换为目标类型的Animation"),
      _CurvedAnimationExample(),
      OlItem(4, "CurvedAnimation"),
      BodyText("曲线动画，对原始动画Animation<double>进行进度值修正，实现加速、减速、回弹"
          "之类的效果"),
      LabelText("- parent，原始动画对象"),
      LabelText(
          "- curve，应用动画曲线，我们默认的动画是线性的（Curves.linear）,Curves中定义了"
              "大量常用的动画曲线，建议直接使用即可"),
      LabelText("- reverseCurve，反转时应用的动画曲线，如果动画没有反转的话可以不设置"),
      OlItem(5, "TrainHoppingAnimation"),
      BodyText("跳火车动画，哈哈，它根据两个动画的当前值，先执行第一个动画，条件达成则执行第二"
          "个动画，感觉也用不到。"),
      LabelText("- 两个动画初始值一致，直接执行第二个动画"),
      LabelText("- 第一个动画初始值比第二个动画高，当第二个动画的值大于第一个动画的值时，切换"
          "到第二个动画"),
      LabelText("- 第一个动画初始值比第二个动画低，当第二个动画的值小于第一个动画的值时，切换"
          "到第二个动画"),
      AppSpacer.large,
      PrimaryTitle("CompoundAnimation 组合动画"),
      BodyText("结合两个动画，在刷新时根据两个动画的当前值来合成新的动画值，这东西很少使用"),
      OlItem(6, "AnimationMean"),
      BodyText("组合两个动画，刷新时取两个动画的平均值"),
      OlItem(7, "AnimationMin"),
      BodyText("组合两个动画，刷新时取两个动画的最小值"),
      OlItem(8, "AnimationMax"),
      BodyText("组合两个动画，刷新时取两个动画的最大值"),
    ];
  }
}

typedef AnimatedWidgetBuilder = Widget Function(
    BuildContext context, Animation<double> animation);

//region 自定义Animation  -------------------------------------------------------
///动画驱动器，我们借助一个状态组件来管理动画生命周期，在生命周期内持续更新动画
class _CustomTimerAnimation extends StatefulWidget {
  final AnimatedWidgetBuilder builder;

  const _CustomTimerAnimation(this.builder, {super.key});

  @override
  State<StatefulWidget> createState() {
    return _CustomTimerAnimationState();
  }
}

///这里用定时器模拟一个动画触发器来更新动画，正常基于时间更新的动画优先考虑使用[AnimationController]
///他会根据屏幕刷新来刷新动画，这里只是个测试例子
class _CustomTimerAnimationState extends State<_CustomTimerAnimation> {
  final ValueNotifier<double> _valueNotifier = ValueNotifier(0.0);
  late Animation<double> _animation;
  late Timer _timer;
  late int _startTime;
  final _duration = const Duration(milliseconds: 2000);

  @override
  void initState() {
    super.initState();
    _animation = Animation.fromValueListenable(_valueNotifier);
    _startTime = DateTime.now().millisecondsSinceEpoch;
    _timer = Timer.periodic(const Duration(milliseconds: 16), (timer) {
      _valueNotifier.value =
          ((DateTime.now().millisecondsSinceEpoch - _startTime) %
                  _duration.inMilliseconds) *
              1.0 /
              _duration.inMilliseconds;
    });
  }

  @override
  void dispose() {
    _timer.cancel();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return widget.builder(context, _animation);
  }
}
//endregion

//region 借助AnimationController来实现平滑的动画效果--------------------------------

///借助AnimationController，结合SingleTickerProviderStateMixin来实现同步屏幕更新的动画
class ControllerAnimation extends StatefulWidget {
  final AnimatedWidgetBuilder builder;

  const ControllerAnimation(this.builder, {super.key});

  @override
  State<StatefulWidget> createState() {
    return _ControllerAnimationState();
  }
}

class _ControllerAnimationState extends State<ControllerAnimation>
    with SingleTickerProviderStateMixin {
  late AnimationController animationController;

  @override
  void initState() {
    super.initState();
    animationController = AnimationController(vsync: this)
      ..duration = const Duration(milliseconds: 2000)
      ..repeat();
  }

  @override
  void dispose() {
    animationController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return widget.builder(context, animationController);
  }
}
//endregion

class _FromValueListenableExample extends StatelessWidget {
  const _FromValueListenableExample();

  @override
  Widget build(BuildContext context) {
    return _CustomTimerAnimation((context, animation) {
      return SlideTransition(
        position: Tween(
          begin: Offset.zero,
          end: const Offset(1, 0),
        ).animate(animation),
        child: const Text("test"),
      );
    });
  }
}

class _TweenExample extends StatelessWidget {
  const _TweenExample();

  @override
  Widget build(BuildContext context) {
    return ControllerAnimation(buildTestAnimationWidget);
  }

  Widget buildTestAnimationWidget(
      BuildContext context, Animation<double> animation) {
    return SlideTransition(
      position: Tween(
        begin: Offset.zero,
        end: const Offset(1, 0),
      ).animate(animation),
      child: const Text("test"),
    );
  }
}

class _CurvedAnimationExample extends _TweenExample {
  const _CurvedAnimationExample();

  @override
  Widget buildTestAnimationWidget(
      BuildContext context, Animation<double> animation) {
    //进行数值修正，将动画效果由线性转为减速
    final curvedAnimation =
        CurvedAnimation(parent: animation, curve: Curves.decelerate);
    return super.buildTestAnimationWidget(context, curvedAnimation);
  }
}
